#ifndef ASF_DIAG_SUPPORT_TIMER_HPP
#define ASF_DIAG_SUPPORT_TIMER_HPP

#include <memory>
#include <chrono>
#include <functional>

namespace DcAdr
{
    class TimerEnginePrivate;
    // forward decalaration
    class TimerPrivate;

    /**
     * \brief The Timer class provides repetitive and single-shot timers.
     *
     * Example:
     * ```cpp
     * Timer timer(true); // single shot timer
     * timer.Start(1000, []() {
     *      std::cout << "timeout 1000 ms interval" << std::endl;
     * });
     * ```
     */
    class Timer
    {
    public:
        /**
         * Constructs a timer.
         */
        Timer();

        /**
         * Constructs a timer with \c single_shot property.
         */
        explicit Timer(bool single_shot);

        /**
         * Destroys the timer.
         */
        ~Timer();

        /**
         * A single-shot timer fires only once, non-single-shot timers fire every interval milliseconds.
         *
         * The default value is `false`.
         *
         * \sa IsSingleShot(), Timer(bool)
         */
        void SetSingleShot(bool single_shot);

        /**
         * Returns \c true if the timer is a single-shot timer, therwise returns `false`.
         *
         * \sa SetSingleShot()
         */
        bool IsSingleShot() const;

        /**
         * Set the intreval for this timer.
         *
         * The default value is `0`.
         *
         * \sa GetInterval()
         */
        void SetInterval(std::int64_t intreval);

        /**
         * Returns the interval of this timer.
         *
         * \sa SetInterval()
         */
        std::int64_t GetInterval() const;

        /**
         * Returns true if the timer is running (pending); otherwise returns false.
         *
         * \sa Start(), Stop()
         */
        bool IsActive() const;

        /**
         * Starts or restarts the timer.
         *
         * If the timer is already running, it will be stopped and restarted.
         * If IsSingleShot() is true, the timer will be activated only once.
         *
         * \sa Stop(), SetInterval(), SetSingleShot(), Start(std::int64_t, const std::function<void()>&)
         */
        void Start(const std::function<void()> &slot);

        /**
         * Starts or restarts the timer with the timeout specified in interval.
         *
         * If the timer is already running, it will be stopped and restarted.
         * If IsSingleShot() is true, the timer will be activated only once.
         *
         * \sa Stop(), SetInterval(), SetSingleShot(), Start(const std::function<void()>&)
         */
        void Start(std::int64_t interval, const std::function<void()> &slot);

        /**
         * Stops the timer.
         *
         * \sa Start()
         */
        void Stop();

        /**
         * Reset the timer to re-timing.
         *
         * \sa Start(), Stop()
         */
        void Reset();

        /**
         * Returns the timer's remaining value in milliseconds left until the timeout.
         *
         * If the timer is inactive, the returned value will be `-1`.
         * If the timer is overdue, the returned value will be `0`.
         */
        std::int64_t GetRemainingTime() const;

    private:
        friend class TimerEnginePrivate;
        std::size_t &GetIdRef();
        std::int64_t GetExpires() const;
        void OnTimeout(std::int64_t time_now);

    private:
        Timer(const Timer &) = delete;
        Timer &operator=(const Timer &) = delete;

    private:
        std::unique_ptr<TimerPrivate> pri_;
    };
} // namespace DcAdr

#endif // ASF_DIAG_SUPPORT_TIMER_HPP